package slap.server;

import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.COOKIE;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.HOST;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import jodd.io.StreamUtil;
import jodd.util.StringUtil;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.http.CookieDecoder;
import org.jboss.netty.handler.codec.http.HttpRequest;

import slap.http.Cookie;
import slap.http.Header;
import slap.http.Request;

public class RequestNettyImpl extends Request {
	
	private HttpRequest wrapped;
	
	private String host;
	private String domain;
	private int port;
	
	private String uri;
	private String path;
	private String paramString;
	private String remoteAddress;
	protected String method;
	private String contentType;
	
	private String rawRequestString;
	
	private boolean secured;
	
	private InputStream body;
	
	private Set<org.jboss.netty.handler.codec.http.Cookie> cookies;
	
	public RequestNettyImpl(HttpRequest nettyRequest, ChannelHandlerContext ctx) throws IOException {
		super();
		wrapped = nettyRequest;
		uri = nettyRequest.getUri();
		int pIndex = uri.indexOf("?");
		// Remove domain and port from URI if it's present.
		if (uri.startsWith("http://") || uri.startsWith("https://")) {
			// Begins searching / after 9th character (last / of https://)
			uri = uri.substring(uri.indexOf("/", 9));
		}
		
		if (pIndex != -1) {
			try {
				path = URLDecoder.decode(uri.substring(0, pIndex), "UTF-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			paramString = uri.substring(pIndex + 1);
		} else {
			try {
				path = URLDecoder.decode(uri, "UTF-8");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			paramString = "";
		}
		
		remoteAddress = header("X-Forwarded-For");
		if(StringUtil.isBlank(remoteAddress)){
			remoteAddress = ((InetSocketAddress) ctx.getChannel().getRemoteAddress()).getAddress().getHostAddress();
		}
		
		method = nettyRequest.getMethod().getName();
		if (nettyRequest.getHeader("X-HTTP-Method-Override") != null) {
			method = nettyRequest.getHeader("X-HTTP-Method-Override").intern();
		}
		
		String nettyContentType = nettyRequest.getHeader(CONTENT_TYPE);
		if (nettyContentType != null) {
			contentType =  nettyContentType.split(";")[0].trim().toLowerCase();
		} else {
			contentType = "text/html";
		}
		

		ChannelBuffer channelBuffer = nettyRequest.getContent();
		if (channelBuffer instanceof FileChannelBuffer) {
			FileChannelBuffer fileChannelBuffer = (FileChannelBuffer) channelBuffer;
			// An error occurred
			int max = -1;
			body = fileChannelBuffer.getInputStream();
			if (!(max == -1 || body.available() < max)) {
				body = new ByteArrayInputStream(new byte[0]);
			}
		} else {
			body = new ChannelBufferInputStream(channelBuffer);
		}
		
		host = nettyRequest.getHeader(HOST);
		if (host == null) {
			host = "";
			port = 80;
			domain = "";
		} else {
			if (host.contains(":")) {
				String[] hosts = host.split(":");
				port = Integer.parseInt(hosts[1]);
				domain = hosts[0];
			} else {
				port = 80;
				domain = host;
			}
		}
		
		secured = false;
		String cs = nettyRequest.getHeader(COOKIE);
		if(cs == null){
			cs = "";
		}
		cookies = new CookieDecoder().decode(cs);
		
		rawRequestString = method.toUpperCase()+" "+uri+" "+wrapped.getProtocolVersion().getText();
	}
	
	@Override
	public String uri() {
		return uri;
	}

	@Override
	public String path() {
		return path;
	}

	@Override
	public String domain() {
		return domain;
	}

	@Override
	public int port() {
		return port;
	}

	@Override
	public String host() {
		return host;
	}

	@Override
	public String paramString() {
		return paramString;
	}

	@Override
	public String parameter(String pname) {
		return null;
	}

	@Override
	public boolean secured() {
		return secured;
	}

	@Override
	public String remoteAddress() {
		return remoteAddress;
	}

	@Override
	public String contentType() {
		return contentType;
	}

	@Override
	public String header(String headerName) {
		return wrapped.getHeader(headerName);
	}

	@Override
	public boolean containsHeader(String headerName) {
		return wrapped.containsHeader(headerName);
	}

	@Override
	public Cookie cookie(String cookieName) {
		for(org.jboss.netty.handler.codec.http.Cookie cookie : cookies){
			if(cookie.getName().equals(cookieName)){
				Cookie slapCookie = new Cookie();
				slapCookie.name = cookie.getName();
				slapCookie.path = cookie.getPath();
				slapCookie.domain = cookie.getDomain();
				slapCookie.secure = cookie.isSecure();
				slapCookie.value = cookie.getValue();
				slapCookie.httpOnly = cookie.isHttpOnly();
				return slapCookie;
			}
		}
		
		return null;
	}

	@Override
	public String method() {
		return method;
	}

	@Override
	public String rawRequestString() {
		return rawRequestString;
	}

}
